package com.atguigu.daijia.map.service.impl;

import com.atguigu.daijia.common.constant.RedisConstant;
import com.atguigu.daijia.common.constant.SystemConstant;
import com.atguigu.daijia.common.util.LocationUtil;
import com.atguigu.daijia.driver.client.DriverInfoFeignClient;
import com.atguigu.daijia.map.repository.OrderServiceLocationRepository;
import com.atguigu.daijia.map.service.LocationService;
import com.atguigu.daijia.model.entity.driver.DriverSet;
import com.atguigu.daijia.model.entity.map.OrderServiceLocation;
import com.atguigu.daijia.model.form.map.OrderServiceLocationForm;
import com.atguigu.daijia.model.form.map.SearchNearByDriverForm;
import com.atguigu.daijia.model.form.map.UpdateDriverLocationForm;
import com.atguigu.daijia.model.form.map.UpdateOrderLocationForm;
import com.atguigu.daijia.model.vo.map.NearByDriverVo;
import com.atguigu.daijia.model.vo.map.OrderLocationVo;
import com.atguigu.daijia.model.vo.map.OrderServiceLastLocationVo;
import com.atguigu.daijia.order.client.OrderInfoFeignClient;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.*;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class LocationServiceImpl implements LocationService {

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private DriverInfoFeignClient driverInfoFeignClient;

    @Override
    public Boolean updateDriverLocation(UpdateDriverLocationForm updateDriverLocationForm) {
        Point point = new Point(updateDriverLocationForm.getLongitude().doubleValue(), updateDriverLocationForm.getLatitude().doubleValue());
        redisTemplate.opsForGeo().add(RedisConstant.DRIVER_GEO_LOCATION,point, updateDriverLocationForm.getDriverId().toString());
        return true;
    }

    @Override
    public Boolean updateOrderLocationCache(UpdateOrderLocationForm updateOrderLocationForm) {
        OrderLocationVo orderLocationVo = new OrderLocationVo();
        orderLocationVo.setLongitude(updateOrderLocationForm.getLongitude());
        orderLocationVo.setLatitude(updateOrderLocationForm.getLatitude());
        redisTemplate.opsForValue().set(RedisConstant.UPDATE_ORDER_LOCATION + updateOrderLocationForm.getOrderId(),orderLocationVo);
        return true;
    }

    @Override
    public OrderLocationVo getCacheOrderLocation(Long orderId) {
        OrderLocationVo orderLocationVo = (OrderLocationVo) redisTemplate.opsForValue().get(RedisConstant.UPDATE_ORDER_LOCATION + orderId);
        return orderLocationVo;
    }

    @Override
    public Boolean updateOrderLocationToCache(UpdateOrderLocationForm updateOrderLocationForm) {
            OrderLocationVo orderLocationVo = new OrderLocationVo();
            orderLocationVo.setLongitude(updateOrderLocationForm.getLongitude());
            orderLocationVo.setLatitude(updateOrderLocationForm.getLatitude());
            redisTemplate.opsForValue().set(RedisConstant.UPDATE_ORDER_LOCATION + updateOrderLocationForm.getOrderId(), orderLocationVo);
            return true;
    }

    @Autowired
    private OrderServiceLocationRepository orderServiceLocationRepository;

    @Override
    public Boolean saveOrderServiceLocation(List<OrderServiceLocationForm> orderServiceLocationFormList) {
        List<OrderServiceLocation> list = new ArrayList<>();
        orderServiceLocationFormList.forEach(item->{
            OrderServiceLocation orderServiceLocation = new OrderServiceLocation();
            orderServiceLocation.setId(ObjectId.get().toString());
            orderServiceLocation.setCreateTime(new Date());
            BeanUtils.copyProperties(item,orderServiceLocation);
            list.add(orderServiceLocation);
        });
        //保存mongodb
        orderServiceLocationRepository.saveAll(list);
        return true;
    }

    @Autowired
    private MongoTemplate mongoTemplate;

    @Override
    public OrderServiceLastLocationVo getOrderServiceLastLocation(Long orderId) {
        //封装查询条件
        Query query = new Query();
        //mongodb 查询
        query.addCriteria(Criteria.where("orderId").is(orderId));
        query.with(Sort.by(Sort.Direction.DESC,"createTime"));
        query.limit(1);
        //开始查询
        OrderServiceLocation orderServiceLocation = mongoTemplate.findOne(query, OrderServiceLocation.class);
        //封装对象返回
        OrderServiceLastLocationVo orderServiceLastLocationVo = new OrderServiceLastLocationVo();
        BeanUtils.copyProperties(orderServiceLocation,orderServiceLastLocationVo);
        return orderServiceLastLocationVo;
    }

    @Autowired
    private OrderInfoFeignClient orderInfoFeignClient;

    @Override
    public BigDecimal calculateOrderRealDistance(Long orderId) {
        //查询mongodb，将所有的位置坐标取出，返回集合
        List<OrderServiceLocation> orderServiceLocationList = orderServiceLocationRepository.findByOrderIdOrderByCreateTimeAsc(orderId);
        double realDistance = 0;
        //将返回的集合进行遍历,获取坐标,得到每一个位置的信息，计算两个位置的距离，将两个距离两两相加
        if(!CollectionUtils.isEmpty(orderServiceLocationList)) {
            //获取集合中的第一个坐标，第二个坐标
            for (int i = 0, size=orderServiceLocationList.size()-1; i < size; i++) {
                OrderServiceLocation location1 = orderServiceLocationList.get(i);
                OrderServiceLocation location2 = orderServiceLocationList.get(i+1);
                //计算两个坐标的距离
                double distance = LocationUtil.getDistance(location1.getLatitude().doubleValue(), location1.getLongitude().doubleValue(), location2.getLatitude().doubleValue(), location2.getLongitude().doubleValue());
                realDistance += distance;
            }
        }
        //测试过程中，没有真正代驾，实际代驾GPS位置没有变化，模拟：实际代驾里程 = 预期里程 + 5
        if(realDistance == 0) {
            return orderInfoFeignClient.getOrderInfo(orderId).getData().getExpectDistance().add(new BigDecimal("5"));
        }
        return new BigDecimal(realDistance);
    }

    @Override
    public Boolean removeDriverLocation(Long driverId) {
        redisTemplate.opsForGeo().remove(RedisConstant.DRIVER_GEO_LOCATION,driverId.toString());
        return true;
    }

    @Override
    public List<NearByDriverVo> searchNearbyDriver(SearchNearByDriverForm searchNearByDriverForm) {
        //搜索经纬度位置5公里的司机
        //1.获取司机的坐标
        Point point = new Point(searchNearByDriverForm.getLongitude().doubleValue(), searchNearByDriverForm.getLatitude().doubleValue());
        //2.定义距离 5公里范围
        Distance distance = new Distance(SystemConstant.NEARBY_DRIVER_RADIUS, RedisGeoCommands.DistanceUnit.KILOMETERS);
        //3.定义point中心为范围
        Circle circle = new Circle(point, distance);
        //4.获取Geo的参数，距离，坐标，排序
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs
                .newGeoRadiusArgs()
                .includeDistance()//包含距离
                .includeCoordinates()//包含坐标
                .sortAscending();//排序
        //4.1 搜素附近范围内的信息
        GeoResults<RedisGeoCommands.GeoLocation<String>> radius = redisTemplate.opsForGeo().radius(RedisConstant.DRIVER_GEO_LOCATION, circle, args);
        //4.2 从redis中获取内容
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> content = radius.getContent();
        //4.2 遍历获取的数据
        ArrayList<NearByDriverVo> list = new ArrayList<>();
        if (!CollectionUtils.isEmpty(content)){
            //遍历获取的内容
            Iterator<GeoResult<RedisGeoCommands.GeoLocation<String>>> iterator = content.iterator();
            while (iterator.hasNext()){
                //判断一下集合中是否有下一个元素
                GeoResult<RedisGeoCommands.GeoLocation<String>> item = iterator.next();
                //获取司机的id
                String name = item.getContent().getName();
                long driverId = Long.parseLong(name);
                //获取司机当前距离
                BigDecimal bigDecimal = new BigDecimal(item.getDistance().getValue()).setScale(2, BigDecimal.ROUND_HALF_UP);
                //获取司机设置的参数
                DriverSet driverSet = driverInfoFeignClient.getDriverSet(driverId).getData();
//                if (driverSet == null){
//                    throw new GuiguException(20001,"获取司机设置信息失败");
//                }
                if (driverSet.getAcceptDistance().doubleValue() != 0 && driverSet.getAcceptDistance().subtract(bigDecimal).doubleValue() < 0){
                    continue;
                }
                //如果司机满足条件则将司机信息
                NearByDriverVo nearByDriverVo = new NearByDriverVo();
                nearByDriverVo.setDriverId(driverId);
                nearByDriverVo.setDistance(bigDecimal);
                list.add(nearByDriverVo);
            }

        }
        return list;
    }












}
